Skip to content

feat(mock): extend output.mock with multi-generator support for msw and faker - #3407

Merged
melloware merged 22 commits into
orval-labs:masterfrom
jakiestfu:feat/refactor-mocks
May 20, 2026
Merged

feat(mock): extend output.mock with multi-generator support for msw and faker#3407
melloware merged 22 commits into
orval-labs:masterfrom
jakiestfu:feat/refactor-mocks

Conversation

@jakiestfu

@jakiestfu jakiestfu commented May 20, 2026

Copy link
Copy Markdown
Contributor

Note on diff size: The +12,152 / -480 delta is dominated by new snapshot and sample output files (~+11,100 lines are generated .faker.ts artifacts). The actual source change is ~+1,044 / -480.

Overview

Fix #1832

Supersedes #3398.

Extends output.mock to accept a multi-generator configuration. Each entry produces its own output file (<filename>.msw.ts, <filename>.faker.ts, etc.), making MSW and Faker first-class peer generators rather than a single toggle with flags.

The generateHandlers: false workaround introduced in #3398 is removed — the same intent is now expressed by including or omitting the appropriate generator entry.

Usage

Shorthand — emit both MSW handlers and Faker factories:

export default defineConfig({
  petstore: {
    output: {
      mock: true,
    },
  },
});

Produces endpoints.msw.ts and endpoints.faker.ts.

Explicit — MSW only:

export default defineConfig({
  petstore: {
    output: {
      mock: {
        generators: [{ type: 'msw', delay: 1000 }],
      },
    },
  },
});

Explicit — Faker only (replaces generateHandlers: false):

export default defineConfig({
  petstore: {
    output: {
      mock: {
        generators: [{ type: 'faker' }],
      },
    },
  },
});

Output contains only response factories — no MSW import, no handler functions, no aggregated array:

import { faker } from '@faker-js/faker';

export const getListPetsResponseMock = (): Pets =>
  Array.from({ length: faker.number.int({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => ({
    id: faker.number.int(),
    name: faker.string.alpha({ length: { min: 10, max: 20 } }),
  }));

Both generators with indexMockFiles:

export default defineConfig({
  petstore: {
    output: {
      mode: 'tags-split',
      mock: {
        indexMockFiles: true,
        generators: [{ type: 'msw' }, { type: 'faker' }],
      },
    },
  },
});

Emits one root-level index file per generator entry:

src/
├── index.msw.ts    # export { getPetsMock } from './pets/pets.msw'
├── index.faker.ts  # export * from './pets/pets.faker'
└── pets/
    ├── pets.msw.ts
    └── pets.faker.ts

Changes

  • output.mock now accepts boolean | { generators: [...], indexMockFiles?: boolean } | Function.
  • GlobalMockOptions is now a discriminated union of MswMockOptions | FakerMockOptions, narrowable via mock.type.
  • OutputMockType enum ('msw' | 'faker') and type guards isMswMock / isFakerMock are exported from @orval/core.
  • mock: true shorthand normalizes to both { type: 'msw' } and { type: 'faker' } generators with defaults.
  • generateHandlers is removed from GlobalMockOptions.
  • indexMockFiles moves from inside the generator options into the mock wrapper object (its natural home as a collection-level concern).
  • Writers loop over mock.generators and emit one file per entry; MSW and Faker files are independent artifacts.
  • Duplicate generator types are rejected at normalization time with a clear error.
  • Function mock generators throw in tags-split mode (unsupported) instead of being silently skipped.
  • All test configs, sample configs, and snapshots are migrated to the new shape.

Migration

- output: { mock: { type: 'msw', generateHandlers: false } }
+ output: { mock: { generators: [{ type: 'faker' }] } }

- output: { mock: { type: 'msw', indexMockFiles: true } }
+ output: { mock: { indexMockFiles: true, generators: [{ type: 'msw' }] } }

Summary by CodeRabbit

  • New Features

    • Multi-provider mock generation: emit MSW handlers and Faker factories in one run; per-generator outputs and index files in tag-split mode.
    • Faker-only generator: emit standalone response factory mocks without MSW handlers; combined MSW+Faker runs supported.
  • Documentation

    • Updated guides, references, examples, CLI help, migration notes and config snippets to the new mocks/generators shape and shorthand.
  • Samples

    • Added many generated Faker-based mock snapshot files for sample projects and tests.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds plural mocks configuration (per-generator MSW/Faker), a Faker generator, core type/option normalization, refactors writers to emit per-generator mock files and indices, updates mock dispatch and utilities, and regenerates docs, samples, and tests.

Changes

Plural mocks config and per-generator emission

Layer / File(s) Summary
Types and config normalization
packages/core/src/types.ts, packages/orval/src/utils/options.ts
Introduce generator-based mock types (OutputMockType.FAKER, NormalizedMocksConfig, etc.) and normalize output.mock into { indexMockFiles, generators }.
Mock provider dispatch (MSW / Faker)
packages/mock/src/index.ts, packages/mock/src/faker/*, packages/mock/src/msw/*
Add Faker generator (generateFaker, generateFakerImports), dispatch generateMock/generateMockImports by OutputMockType, and update MSW generation to use type guards.
Type guards & utilities
packages/core/src/utils/assertion.ts, packages/core/src/utils/file-extensions.ts, packages/core/src/writers/mock-outputs.ts
Add isMswMock/isFakerMock, return typed mock file extension by type, and helper collapseInlineMockOutputs.
Writers: single/split/tags/tags-split/target
packages/core/src/writers/*.ts, packages/core/src/writers/target.ts, packages/core/src/writers/target-tags.ts
Refactor to aggregate mockOutputs, emit one mock file per generator/type, create per-generator index files in tags-split, and collapse inline Faker outputs when MSW is present.
Client & generation plumbing
packages/orval/src/client.ts, packages/orval/src/api.ts, packages/hono/src/index.ts, packages/mcp/src/index.ts
Run configured mock generators per operation, store mockOutputs instead of implementationMock/importsMock, and stop passing mock: output.mock into downstream generators.
Mock runtime helpers
packages/mock/src/delay.ts
Narrow MSW-only options using isMswMock and specialize delay typing/logic.
Tests & snapshots
tests/**, samples/**, packages/*/*.test.ts
Add Faker-specific tests, update test helpers to new normalized mock defaults, and regenerate many snapshots to include *.faker.ts.
Docs & CLI
docs/**, skills/orval/*, packages/orval/src/bin/orval.ts
Document mocks/generators shape, mock: true shorthand now enabling MSW+Faker, indexMockFiles semantics, and update CLI help text.

Sequence Diagram(s)

sequenceDiagram
  participant User as User Config
  participant Normalize as Orval Normalize
  participant Generator as Generator Dispatch
  participant Writers as File Writers
  User->>Normalize: provide `mocks` / `mock.generators`
  Normalize->>Generator: normalized generators list
  Generator->>Writers: operations + mockOutputs (per type)
  Writers->>Writers: per-generator file/indices assembly
  Writers->>Writers: collapse inline Faker when MSW present
  Writers->>User: written files (*.msw.ts, *.faker.ts, index.<ext>.ts)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related issues

Possibly related PRs

Suggested labels

msw

Suggested reviewers

  • melloware

Poem

"I'm a rabbit in the codewood, quick and spry,
I planted Faker seeds and watched mocks multiply.
MSW and Faker dance in pairs, files bloom two by two,
Tags split neatly, indices sing — snapshots all brand-new.
Hop, run, generate — the tests applaud, hooray!"

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

@jakiestfu jakiestfu changed the title feat(): refactor mocks feat(core): refactor mocks May 20, 2026
@jakiestfu jakiestfu changed the title feat(core): refactor mocks feat(mock): replace output.mock with output.mocks array supporting msw and faker generators May 20, 2026
@melloware melloware added breaking change Breaking change on upgrade mock Related to mock generation labels May 20, 2026
@jakiestfu
jakiestfu force-pushed the feat/refactor-mocks branch from dc4d801 to 095c898 Compare May 20, 2026 20:32
@jakiestfu
jakiestfu marked this pull request as ready for review May 20, 2026 20:32

@melloware melloware left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jakiestfu sorry but can you keep it sinular mock not mocks for backward compat so 90% of people who have mock: true will get their expected out.

I think its OK its not plural its configuring mock generation

@jakiestfu

Copy link
Copy Markdown
Contributor Author

@melloware CC @haydencrain @ingvaldlorentzen

I know there is some sticker shock in this PRs delta, but I've reviewed the code myself in packages/core and it seems pretty nominal. tons of snapshots, obviously.

Thoughts? Feedback?

@melloware

Copy link
Copy Markdown
Collaborator

so I gave you one thought about about leaving it mock singular but also make sure can you add a section to the V8 migration guide: https://orval.dev/docs/versions/v8

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
docs/content/docs/guides/client-with-zod.mdx (1)

51-59: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Incomplete file structure documentation.

The generated files section shows only pets.msw.ts (line 55), but according to the PR objectives, mocks: true emits both MSW and Faker files. The documentation should also list pets.faker.ts to reflect the actual output.

📝 Suggested addition to file structure
 src/api/
 ├── endpoints/
 │   └── pets/
 │       ├── pets.ts       # SWR hooks
 │       ├── pets.msw.ts   # MSW mocks
+│       ├── pets.faker.ts # Faker mocks
 │       └── pets.zod.ts   # Zod schemas
 └── models/
     └── ...
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/content/docs/guides/client-with-zod.mdx` around lines 51 - 59, The docs
show only pets.msw.ts under src/api/endpoints/pets but the generator also emits
Faker files when mocks: true; update the file structure example to include
pets.faker.ts alongside pets.msw.ts (keeping pets.ts and pets.zod.ts) so it
accurately reflects the output; edit the snippet that lists
src/api/endpoints/pets to add an entry for pets.faker.ts and ensure
ordering/notes match the existing style.
docs/content/docs/guides/basics.mdx (1)

40-40: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update table to reference mocks instead of mock.

The configuration example on line 21 correctly uses mocks: true, but this table row still references the old mock key, creating an inconsistency in the documentation.

📝 Proposed fix
-| `mock` | Generates mocks with MSW. See the [MSW guide](/docs/guides/msw) |
+| `mocks` | Generates mocks with MSW. See the [MSW guide](/docs/guides/msw) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/content/docs/guides/basics.mdx` at line 40, Update the table row that
currently references the configuration key `mock` to use `mocks` so it matches
the example earlier (`mocks: true`); locate the table cell containing the
backticked `mock` symbol and replace it with `mocks` (keeping the MSW guide link
and surrounding text unchanged) to remove the inconsistency.
🧹 Nitpick comments (7)
packages/mock/src/faker/index.test.ts (1)

74-81: ⚡ Quick win

Strengthen the equivalence assertion to match the test intent.

This test currently validates only that implementation.function is a string, not that it matches the MSW output as the title/comment claims. Compare against the MSW-generated function string directly to actually guard behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/mock/src/faker/index.test.ts` around lines 74 - 81, The test
currently only asserts typeof fakerResult.implementation.function is 'string'
but should verify equivalence to the MSW output; call generate with
OutputMockType.MSW (e.g., const mswResult = generate({ mock: { type:
OutputMockType.MSW } })) and assert that fakerResult.implementation.function ===
mswResult.implementation.function so the faker generator truly matches MSW's
emitted function string; reference the generate function, OutputMockType,
fakerResult, mswResult, and implementation.function when making the change.
samples/svelte-query/basic/__snapshots__/endpoints/petstoreFromFileSpecWithTransformer.faker.ts (2)

21-26: ⚡ Quick win

Remove unnecessary IIFE wrappers.

The outer (() => ({ ... }))() wrapper serves no purpose here and can be removed for cleaner generated code.

♻️ Simplified function
-export const getShowPetByIdResponseMock = () =>
-  (() => ({
-    id: faker.number.int({ min: 1, max: 99 }),
-    name: faker.person.firstName(),
-    tag: faker.helpers.arrayElement([faker.string.sample(), undefined]),
-  }))();
+export const getShowPetByIdResponseMock = () => ({
+  id: faker.number.int({ min: 1, max: 99 }),
+  name: faker.person.firstName(),
+  tag: faker.helpers.arrayElement([faker.string.sample(), undefined]),
+});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@samples/svelte-query/basic/__snapshots__/endpoints/petstoreFromFileSpecWithTransformer.faker.ts`
around lines 21 - 26, The function getShowPetByIdResponseMock contains an
unnecessary immediately-invoked function expression (IIFE) wrapper around the
returned object; remove the outer (() => ({ ... }))() and have
getShowPetByIdResponseMock directly return the object literal (id, name, tag) so
the code is simpler and functionally identical.

11-19: ⚡ Quick win

Simplify Array.from mapper to avoid creating unused sequential values.

The current pattern creates an array [1, 2, 3, ..., N] via Array.from({ length }, (_, i) => i + 1), but the subsequent .map() immediately discards these values. The mapper in Array.from can directly create the final objects.

♻️ Simplified array generation
 export const getListPetsResponseMock = (): Pets =>
   Array.from(
     { length: faker.number.int({ min: 1, max: 10 }) },
-    (_, i) => i + 1,
-  ).map(() => ({
+  ).map(() => ({
     id: (() => faker.number.int({ min: 1, max: 99999 }))(),
     name: (() => faker.person.lastName())(),
     tag: (() => faker.person.lastName())(),
   }));

Or use the mapper directly:

 export const getListPetsResponseMock = (): Pets =>
   Array.from(
     { length: faker.number.int({ min: 1, max: 10 }) },
+    () => ({
+      id: faker.number.int({ min: 1, max: 99999 }),
+      name: faker.person.lastName(),
+      tag: faker.person.lastName(),
+    }),
-    (_, i) => i + 1,
-  ).map(() => ({
-    id: (() => faker.number.int({ min: 1, max: 99999 }))(),
-    name: (() => faker.person.lastName())(),
-    tag: (() => faker.person.lastName())(),
-  }));
+  );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@samples/svelte-query/basic/__snapshots__/endpoints/petstoreFromFileSpecWithTransformer.faker.ts`
around lines 11 - 19, The getListPetsResponseMock function builds a sequential
array then immediately maps over it to create pet objects; change Array.from({
length: faker.number.int(...) }, (_, i) => i + 1).map(...) to use Array.from({
length: faker.number.int(...) }, () => ({ id: faker.number.int({ min: 1, max:
99999 }), name: faker.person.lastName(), tag: faker.person.lastName() })) so the
mapper directly returns the pet objects and you can remove the extra .map().
samples/svelte-query/basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.faker.ts (2)

11-19: ⚡ Quick win

Simplify Array.from mapper to avoid creating unused sequential values.

The current pattern creates an array [1, 2, 3, ..., N] via Array.from({ length }, (_, i) => i + 1), but the subsequent .map() immediately discards these values. The mapper in Array.from can directly create the final objects.

♻️ Simplified array generation
 export const getListPetsResponseMock = (): Pets =>
   Array.from(
     { length: faker.number.int({ min: 1, max: 10 }) },
-    (_, i) => i + 1,
-  ).map(() => ({
+  ).map(() => ({
     id: (() => faker.number.int({ min: 1, max: 99999 }))(),
     name: (() => faker.person.lastName())(),
     tag: (() => faker.person.lastName())(),
   }));

Or even better, use the mapper directly:

 export const getListPetsResponseMock = (): Pets =>
   Array.from(
     { length: faker.number.int({ min: 1, max: 10 }) },
+    () => ({
+      id: faker.number.int({ min: 1, max: 99999 }),
+      name: faker.person.lastName(),
+      tag: faker.person.lastName(),
+    }),
-    (_, i) => i + 1,
-  ).map(() => ({
-    id: (() => faker.number.int({ min: 1, max: 99999 }))(),
-    name: (() => faker.person.lastName())(),
-    tag: (() => faker.person.lastName())(),
-  }));
+  );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@samples/svelte-query/basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.faker.ts`
around lines 11 - 19, The getListPetsResponseMock function builds an
intermediate numeric array then maps it away; replace the two-step
Array.from(...).map(...) with a single Array.from call that creates the pet
objects directly (e.g., Array.from({ length: faker.number.int({ min: 1, max: 10
}) }, () => ({ id: faker.number.int({ min: 1, max: 99999 }), name:
faker.person.lastName(), tag: faker.person.lastName() }))). Update
getListPetsResponseMock to use this direct mapper so there are no unused
sequential values.

21-26: ⚡ Quick win

Remove unnecessary IIFE wrappers.

The outer (() => ({ ... }))() wrapper and individual property IIFEs serve no purpose here and can be removed for cleaner generated code.

♻️ Simplified function
-export const getShowPetByIdResponseMock = () =>
-  (() => ({
-    id: faker.number.int({ min: 1, max: 99 }),
-    name: faker.person.firstName(),
-    tag: faker.helpers.arrayElement([faker.string.sample(), undefined]),
-  }))();
+export const getShowPetByIdResponseMock = () => ({
+  id: faker.number.int({ min: 1, max: 99 }),
+  name: faker.person.firstName(),
+  tag: faker.helpers.arrayElement([faker.string.sample(), undefined]),
+});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@samples/svelte-query/basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.faker.ts`
around lines 21 - 26, The getShowPetByIdResponseMock function uses an
unnecessary outer IIFE (and individual property IIFEs per the review); replace
the outer wrapper by returning the object literal directly from the arrow
function and ensure each property calls faker directly (i.e., change export
const getShowPetByIdResponseMock = () => (() => ({ ... }))() to export const
getShowPetByIdResponseMock = () => ({ id: faker.number.int(...), name:
faker.person.firstName(), tag: faker.helpers.arrayElement([...]) }); so there
are no self-invoking functions around the object or its properties.
samples/react-app/__snapshots__/endpoints/petstoreFromFileSpecWithTransformer.faker.ts (2)

11-19: ⚡ Quick win

Simplify Array.from mapper to avoid creating unused sequential values.

The current pattern creates an array [1, 2, 3, ..., N] via Array.from({ length }, (_, i) => i + 1), but the subsequent .map() immediately discards these values. The mapper in Array.from can directly create the final objects.

♻️ Simplified array generation
 export const getListPetsResponseMock = (): Pets =>
   Array.from(
     { length: faker.number.int({ min: 1, max: 10 }) },
-    (_, i) => i + 1,
-  ).map(() => ({
+  ).map(() => ({
     id: (() => faker.number.int({ min: 1, max: 99999 }))(),
     name: (() => faker.person.lastName())(),
     tag: (() => faker.person.lastName())(),
   }));

Or use the mapper directly:

 export const getListPetsResponseMock = (): Pets =>
   Array.from(
     { length: faker.number.int({ min: 1, max: 10 }) },
+    () => ({
+      id: faker.number.int({ min: 1, max: 99999 }),
+      name: faker.person.lastName(),
+      tag: faker.person.lastName(),
+    }),
-    (_, i) => i + 1,
-  ).map(() => ({
-    id: (() => faker.number.int({ min: 1, max: 99999 }))(),
-    name: (() => faker.person.lastName())(),
-    tag: (() => faker.person.lastName())(),
-  }));
+  );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@samples/react-app/__snapshots__/endpoints/petstoreFromFileSpecWithTransformer.faker.ts`
around lines 11 - 19, The generated mock getListPetsResponseMock creates a
sequential array via Array.from(..., (_, i) => i + 1) and then discards those
values with a subsequent .map(); change Array.from in getListPetsResponseMock to
use the mapper to directly produce each Pets object (use Array.from({ length:
faker.number.int({ min: 1, max: 10 }) }, () => ({ id: faker.number.int({ min: 1,
max: 99999 }), name: faker.person.lastName(), tag: faker.person.lastName() })))
so you no longer need the extra .map() and eliminate the unused sequential
values.

21-26: ⚡ Quick win

Remove unnecessary IIFE wrappers.

The outer (() => ({ ... }))() wrapper serves no purpose here and can be removed for cleaner generated code.

♻️ Simplified function
-export const getShowPetByIdResponseMock = () =>
-  (() => ({
-    id: faker.number.int({ min: 1, max: 99 }),
-    name: faker.person.firstName(),
-    tag: faker.helpers.arrayElement([faker.word.sample(), undefined]),
-  }))();
+export const getShowPetByIdResponseMock = () => ({
+  id: faker.number.int({ min: 1, max: 99 }),
+  name: faker.person.firstName(),
+  tag: faker.helpers.arrayElement([faker.word.sample(), undefined]),
+});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@samples/react-app/__snapshots__/endpoints/petstoreFromFileSpecWithTransformer.faker.ts`
around lines 21 - 26, The generated mock function getShowPetByIdResponseMock
contains an unnecessary immediately-invoked function expression; remove the
outer (() => ({ ... }))() wrapper so the function directly returns the object
literal (id, name, tag) instead of invoking an IIFE—i.e., change
getShowPetByIdResponseMock to return the faker-generated object directly without
the extra parentheses/IIFE.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/content/docs/reference/integration.mdx`:
- Line 78: The commented example uses the old flat "mock" config; update the
comment to show the new API that wraps generator configs in a generators array
(e.g., replace the old "// mock: { type: 'msw', delay: 1000 }" example with an
explicit generators array entry such as "// generators: [{ type: 'msw', delay:
1000 }]" so the docs reflect the new explicit generator configuration
structure).

In `@packages/core/src/writers/split-tags-mode.ts`:
- Around line 58-64: The loop building indexFilePathsByType with
generatorEntries uses the mock-type extension (via
getMockFileExtensionByTypeName) as the sole key so multiple generator entries
with the same type overwrite each other; change the logic to handle duplicate
types by making the key unique per entry (e.g., include a per-entry identifier
like entry.name or entry.id) or accumulate paths per-extension
(Map<string,string[]>), and only write/initialize an index path when creating a
new unique path (or deduplicate before writing). Update the same pattern in the
other affected blocks that reference indexFilePathsByType/generatorEntries (the
other write/export loops) so all places either check for existing entries before
overwriting or switch to storing arrays of paths and iterate them when emitting
indexes.
- Around line 243-250: The loop that builds mock files silently skips
function-based generator entries (mockOutputs loop inspecting
output.mocks.generators and using isFunction), causing missing files; update the
loop in the split-tags logic (the for ... of mockOutputs.entries() block) to
detect when entry is a function and throw a clear, descriptive error (e.g.,
"Function-based mock generators are not supported in tags-split mode") instead
of continue, so callers fail fast until ClientMockBuilder functions are
implemented.

In `@packages/core/src/writers/target.ts`:
- Around line 66-71: The code incorrectly collapses multiple generator entries
by matching target.mockOutputs by type (using find) which loses per-entry
options/files; instead, for each operation.mockOutputs entry create a distinct
mock output instance rather than reusing one by type: remove the find((m) =>
m.type === opMock.type) merge, call emptyMockOutputFull(opMock.type) for each
opMock, push that new instance into target.mockOutputs, and then copy/merge the
specific per-entry fields (options/files/etc.) from operation.mockOutputs into
the newly created instance so the new output.mocks.generators model preserves
per-entry configuration.

In `@packages/mock/src/index.ts`:
- Around line 29-39: The switch in getDefaultMockOptionsForType uses case
clauses without braces which triggers unicorn/switch-case-braces; wrap each case
body in braces (e.g., case OutputMockType.FAKER: { return DEFAULT_FAKER_OPTIONS;
} ) and do the same for the other two switch statements in this file so each
case (including default) has its own block, or refactor to equivalent if/else
blocks; update getDefaultMockOptionsForType and the other switch-using functions
in the module accordingly.

---

Outside diff comments:
In `@docs/content/docs/guides/basics.mdx`:
- Line 40: Update the table row that currently references the configuration key
`mock` to use `mocks` so it matches the example earlier (`mocks: true`); locate
the table cell containing the backticked `mock` symbol and replace it with
`mocks` (keeping the MSW guide link and surrounding text unchanged) to remove
the inconsistency.

In `@docs/content/docs/guides/client-with-zod.mdx`:
- Around line 51-59: The docs show only pets.msw.ts under src/api/endpoints/pets
but the generator also emits Faker files when mocks: true; update the file
structure example to include pets.faker.ts alongside pets.msw.ts (keeping
pets.ts and pets.zod.ts) so it accurately reflects the output; edit the snippet
that lists src/api/endpoints/pets to add an entry for pets.faker.ts and ensure
ordering/notes match the existing style.

---

Nitpick comments:
In `@packages/mock/src/faker/index.test.ts`:
- Around line 74-81: The test currently only asserts typeof
fakerResult.implementation.function is 'string' but should verify equivalence to
the MSW output; call generate with OutputMockType.MSW (e.g., const mswResult =
generate({ mock: { type: OutputMockType.MSW } })) and assert that
fakerResult.implementation.function === mswResult.implementation.function so the
faker generator truly matches MSW's emitted function string; reference the
generate function, OutputMockType, fakerResult, mswResult, and
implementation.function when making the change.

In
`@samples/react-app/__snapshots__/endpoints/petstoreFromFileSpecWithTransformer.faker.ts`:
- Around line 11-19: The generated mock getListPetsResponseMock creates a
sequential array via Array.from(..., (_, i) => i + 1) and then discards those
values with a subsequent .map(); change Array.from in getListPetsResponseMock to
use the mapper to directly produce each Pets object (use Array.from({ length:
faker.number.int({ min: 1, max: 10 }) }, () => ({ id: faker.number.int({ min: 1,
max: 99999 }), name: faker.person.lastName(), tag: faker.person.lastName() })))
so you no longer need the extra .map() and eliminate the unused sequential
values.
- Around line 21-26: The generated mock function getShowPetByIdResponseMock
contains an unnecessary immediately-invoked function expression; remove the
outer (() => ({ ... }))() wrapper so the function directly returns the object
literal (id, name, tag) instead of invoking an IIFE—i.e., change
getShowPetByIdResponseMock to return the faker-generated object directly without
the extra parentheses/IIFE.

In
`@samples/svelte-query/basic/__snapshots__/endpoints/petstoreFromFileSpecWithTransformer.faker.ts`:
- Around line 21-26: The function getShowPetByIdResponseMock contains an
unnecessary immediately-invoked function expression (IIFE) wrapper around the
returned object; remove the outer (() => ({ ... }))() and have
getShowPetByIdResponseMock directly return the object literal (id, name, tag) so
the code is simpler and functionally identical.
- Around line 11-19: The getListPetsResponseMock function builds a sequential
array then immediately maps over it to create pet objects; change Array.from({
length: faker.number.int(...) }, (_, i) => i + 1).map(...) to use Array.from({
length: faker.number.int(...) }, () => ({ id: faker.number.int({ min: 1, max:
99999 }), name: faker.person.lastName(), tag: faker.person.lastName() })) so the
mapper directly returns the pet objects and you can remove the extra .map().

In
`@samples/svelte-query/basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.faker.ts`:
- Around line 11-19: The getListPetsResponseMock function builds an intermediate
numeric array then maps it away; replace the two-step Array.from(...).map(...)
with a single Array.from call that creates the pet objects directly (e.g.,
Array.from({ length: faker.number.int({ min: 1, max: 10 }) }, () => ({ id:
faker.number.int({ min: 1, max: 99999 }), name: faker.person.lastName(), tag:
faker.person.lastName() }))). Update getListPetsResponseMock to use this direct
mapper so there are no unused sequential values.
- Around line 21-26: The getShowPetByIdResponseMock function uses an unnecessary
outer IIFE (and individual property IIFEs per the review); replace the outer
wrapper by returning the object literal directly from the arrow function and
ensure each property calls faker directly (i.e., change export const
getShowPetByIdResponseMock = () => (() => ({ ... }))() to export const
getShowPetByIdResponseMock = () => ({ id: faker.number.int(...), name:
faker.person.firstName(), tag: faker.helpers.arrayElement([...]) }); so there
are no self-invoking functions around the object or its properties.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7266088c-c0b7-49a6-8ea3-09c5d22e6821

📥 Commits

Reviewing files that changed from the base of the PR and between 90532f4 and 095c898.

⛔ Files ignored due to path filters (7)
  • samples/hono/hono-with-fetch-client/next-app/app/gen/pets/pets.faker.ts is excluded by !**/gen/**
  • samples/next-app-with-fetch/app/gen/pets/pets.faker.ts is excluded by !**/gen/**
  • samples/react-query/custom-fetch/src/gen/pets/pets.faker.ts is excluded by !**/gen/**
  • samples/svelte-query/custom-fetch/src/gen/pets/pets.faker.ts is excluded by !**/gen/**
  • samples/svelte-query/v6-custom-fetch/src/gen/pets/pets.faker.ts is excluded by !**/gen/**
  • samples/swr-with-zod/src/gen/endpoints/pets/pets.faker.ts is excluded by !**/gen/**
  • samples/vue-query/custom-fetch/src/gen/pets/pets.faker.ts is excluded by !**/gen/**
📒 Files selected for processing (134)
  • docs/content/docs/guides/angular-query.mdx
  • docs/content/docs/guides/angular.mdx
  • docs/content/docs/guides/basics.mdx
  • docs/content/docs/guides/client-with-zod.mdx
  • docs/content/docs/guides/fetch.mdx
  • docs/content/docs/guides/msw.mdx
  • docs/content/docs/guides/react-query.mdx
  • docs/content/docs/guides/solid-query.mdx
  • docs/content/docs/guides/solid-start.mdx
  • docs/content/docs/guides/svelte-query.mdx
  • docs/content/docs/guides/swr.mdx
  • docs/content/docs/guides/vue-query.mdx
  • docs/content/docs/reference/configuration/full-example.mdx
  • docs/content/docs/reference/configuration/output.mdx
  • docs/content/docs/reference/integration.mdx
  • packages/angular/src/http-client.test.ts
  • packages/angular/src/http-resource.test.ts
  • packages/core/src/test-utils/context.ts
  • packages/core/src/test-utils/split-modes.ts
  • packages/core/src/types.ts
  • packages/core/src/utils/assertion.ts
  • packages/core/src/utils/file-extensions.ts
  • packages/core/src/writers/mock-outputs.ts
  • packages/core/src/writers/single-mode.ts
  • packages/core/src/writers/split-mode.ts
  • packages/core/src/writers/split-tags-mode.ts
  • packages/core/src/writers/tags-mode.ts
  • packages/core/src/writers/target-tags.ts
  • packages/core/src/writers/target.ts
  • packages/hono/src/index.ts
  • packages/mcp/src/index.ts
  • packages/mock/src/delay.ts
  • packages/mock/src/faker/getters/combine.test.ts
  • packages/mock/src/faker/index.test.ts
  • packages/mock/src/faker/index.ts
  • packages/mock/src/index.ts
  • packages/mock/src/msw/index.test.ts
  • packages/mock/src/msw/index.ts
  • packages/orval/src/api.ts
  • packages/orval/src/bin/orval.ts
  • packages/orval/src/client.ts
  • packages/orval/src/utils/options.ts
  • packages/orval/src/write-specs.ts
  • packages/solid-start/src/index.test.ts
  • samples/angular-app/orval.config.ts
  • samples/angular-query/orval.config.ts
  • samples/basic/orval.config.ts
  • samples/hono/hono-with-fetch-client/__snapshots__/next-app/pets/pets.faker.ts
  • samples/hono/hono-with-fetch-client/orval.config.ts
  • samples/next-app-with-fetch/__snapshots__/pets/pets.faker.ts
  • samples/next-app-with-fetch/orval.config.ts
  • samples/react-app-with-swr/basic/__snapshots__/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • samples/react-app-with-swr/basic/orval.config.ts
  • samples/react-app-with-swr/basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • samples/react-app-with-swr/fetch-client/__snapshots__/endpoints/swaggerPetstore.faker.ts
  • samples/react-app-with-swr/fetch-client/orval.config.ts
  • samples/react-app-with-swr/fetch-client/src/api/endpoints/swaggerPetstore.faker.ts
  • samples/react-app/__snapshots__/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • samples/react-app/orval.config.ts
  • samples/react-app/src/api/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • samples/react-query/basic/__snapshots__/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • samples/react-query/basic/orval.config.ts
  • samples/react-query/basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • samples/react-query/custom-fetch/__snapshots__/pets/pets.faker.ts
  • samples/react-query/custom-fetch/orval.config.ts
  • samples/solid-query/basic/orval.config.ts
  • samples/solid-query/custom-fetch/orval.config.ts
  • samples/solid-start/basic/orval.config.ts
  • samples/solid-start/basic/src/api/endpoints/petstore.faker.ts
  • samples/svelte-query/basic/__snapshots__/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • samples/svelte-query/basic/orval.config.ts
  • samples/svelte-query/basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • samples/svelte-query/custom-fetch/__snapshots__/pets/pets.faker.ts
  • samples/svelte-query/custom-fetch/orval.config.ts
  • samples/svelte-query/v6-custom-fetch/__snapshots__/pets/pets.faker.ts
  • samples/svelte-query/v6-custom-fetch/orval.config.ts
  • samples/swr-with-zod/__snapshots__/endpoints/pets/pets.faker.ts
  • samples/swr-with-zod/orval.config.ts
  • samples/vue-query/custom-fetch/__snapshots__/pets/pets.faker.ts
  • samples/vue-query/custom-fetch/orval.config.ts
  • samples/vue-query/vue-query-basic/__snapshots__/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • samples/vue-query/vue-query-basic/orval.config.ts
  • samples/vue-query/vue-query-basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • skills/orval/SKILL.md
  • skills/orval/advanced-config.md
  • skills/orval/angular.md
  • skills/orval/hono.md
  • skills/orval/mocking-msw.md
  • skills/orval/solid-start.md
  • skills/orval/tooling-workflow.md
  • tests/__snapshots__/angular/split/endpoints.faker.ts
  • tests/__snapshots__/angular/tags-split/health/health.faker.ts
  • tests/__snapshots__/angular/tags-split/pets/pets.faker.ts
  • tests/__snapshots__/axios/petstore-tags-split-mutator/health/health.faker.ts
  • tests/__snapshots__/axios/petstore-tags-split-mutator/pets/pets.faker.ts
  • tests/__snapshots__/axios/petstore-tags-split/health/health.faker.ts
  • tests/__snapshots__/axios/petstore-tags-split/pets/pets.faker.ts
  • tests/__snapshots__/axios/split/endpoints.faker.ts
  • tests/__snapshots__/default/issue-2998/documents/documents.faker.ts
  • tests/__snapshots__/default/issue-2998/requests/requests.faker.ts
  • tests/__snapshots__/default/override-mock/endpoints.faker.ts
  • tests/__snapshots__/fetch/petstore-tags-split/health/health.faker.ts
  • tests/__snapshots__/fetch/petstore-tags-split/pets/pets.faker.ts
  • tests/__snapshots__/fetch/split/endpoints.faker.ts
  • tests/__snapshots__/mock/petstore-tags-split/health/health.faker.ts
  • tests/__snapshots__/mock/petstore-tags-split/pets/pets.faker.ts
  • tests/__snapshots__/mock/split/endpoints.faker.ts
  • tests/__snapshots__/react-query/importFromSubdirectory/endpoints.faker.ts
  • tests/__snapshots__/react-query/petstore-tags-split/health/health.faker.ts
  • tests/__snapshots__/react-query/petstore-tags-split/pets/pets.faker.ts
  • tests/__snapshots__/react-query/split/endpoints.faker.ts
  • tests/__snapshots__/svelte-query/petstore-tags-split/health/health.faker.ts
  • tests/__snapshots__/svelte-query/petstore-tags-split/pets/pets.faker.ts
  • tests/__snapshots__/svelte-query/split/endpoints.faker.ts
  • tests/__snapshots__/swr/petstore-tags-split/health/health.faker.ts
  • tests/__snapshots__/swr/petstore-tags-split/pets/pets.faker.ts
  • tests/__snapshots__/swr/split/endpoints.faker.ts
  • tests/__snapshots__/vue-query/petstore-tags-split/health/health.faker.ts
  • tests/__snapshots__/vue-query/petstore-tags-split/pets/pets.faker.ts
  • tests/__snapshots__/vue-query/split/endpoints.faker.ts
  • tests/__snapshots__/zod/petstore-tags-split/pets/pets.faker.ts
  • tests/__snapshots__/zod/split/endpoints.faker.ts
  • tests/configs/angular.config.ts
  • tests/configs/axios.config.ts
  • tests/configs/default.config.ts
  • tests/configs/fetch.config.ts
  • tests/configs/mock.config.ts
  • tests/configs/react-query.config.ts
  • tests/configs/solid-query.config.ts
  • tests/configs/solid-start.config.ts
  • tests/configs/svelte-query.config.ts
  • tests/configs/swr.config.ts
  • tests/configs/vue-query.config.ts
  • tests/configs/zod.config.ts
💤 Files with no reviewable changes (3)
  • packages/orval/src/api.ts
  • packages/mcp/src/index.ts
  • packages/hono/src/index.ts

Comment thread docs/content/docs/reference/integration.mdx Outdated
Comment thread packages/core/src/writers/split-tags-mode.ts
Comment thread packages/core/src/writers/split-tags-mode.ts
Comment thread packages/core/src/writers/target.ts
Comment thread packages/mock/src/index.ts
@jakiestfu jakiestfu changed the title feat(mock): replace output.mock with output.mocks array supporting msw and faker generators feat(mock): extend output.mock with multi-generator support for msw and faker May 20, 2026
@jakiestfu
jakiestfu requested a review from melloware May 20, 2026 21:01
jakiestfu added 17 commits May 20, 2026 14:02
…MockOptions union

Make GlobalMockOptions a discriminated union over OutputMockType (MSW | FAKER)
so consumers can narrow per-generator settings. Hoist shared fields into
CommonMockOptions and move msw-only fields (baseUrl, delay,
delayFunctionLazyExecute) onto MswMockOptions.

Introduce OutputMocksConfig and NormalizedMocksConfig to back the new
output.mocks shape (boolean | OutputMocksConfig | ClientMockBuilder), and
replace output.mock / NormalizedOutputOptions.mock / GlobalOptions.mock with
output.mocks. GeneratorTarget(Full)/GeneratorOperation gain mockOutputs arrays
keyed by mock type so writers can emit one file per configured generator.

Drive getMockFileExtensionByTypeName from mock.type so each entry's filename
suffix (.msw.ts, .faker.ts) matches its configured generator. Remove the
GlobalMockOptions.generateHandlers flag - omitting an MSW generator entry now
expresses the same intent.
Add packages/mock/src/faker/index.ts exposing generateFaker and
generateFakerImports. The faker generator reuses the MSW response-factory
output and strips the handler payload so it can emit a standalone
<file>.faker.ts with no msw dependency.

Replace DEFAULT_MOCK_OPTIONS with per-type DEFAULT_MSW_OPTIONS /
DEFAULT_FAKER_OPTIONS plus a getDefaultMockOptionsForType helper used by
the normalizer. The top-level generateMock and generateMockImports now
dispatch on OutputMockType so each entry in output.mocks.generators is
routed to the correct generator.

Narrow GlobalMockOptions via isMswMock in delay.ts and msw/index.ts so
msw-only fields (delay, baseUrl, delayFunctionLazyExecute) are no longer
read off faker entries. Drop the generateHandlers branch from generateMSW
and getMSWDependencies.
Replace the singular implementationMock / importsMock fields on
GeneratorTarget(Full) and GeneratorOperation with GeneratorMockOutput arrays
keyed by OutputMockType. target.ts and target-tags.ts accumulate one
GeneratorMockOutputFull per generator entry, applying the header/footer
handler wrapper only to entries that produced handler aggregator output
(so faker-only entries stay handler-free).

Writers now iterate target.mockOutputs and emit a separate file per entry.
split-mode and split-tags-mode use getMockFileExtensionByTypeName(entry) for
the per-file suffix (.msw.ts / .faker.ts). single-mode and tags-mode still
inline the mocks into the implementation file but call importsMock once per
entry so each entry's import header reflects its own generator.

split-tags-mode pre-creates one index.<ext>.ts per generator when
output.mocks.indexMockFiles is true. MSW entries keep the named-aggregator
re-export shape (export { getTagMock } from ...) so existing
index.msw.ts consumers keep working; faker entries use export * since
faker has no aggregator handler.

Update test-utils fixtures (createSplitModeOperation, createTestContextSpec)
to use the new mocks shape and mockOutputs array.
Rework normalizeOptions to coerce the new output.mocks shape (boolean,
ClientMockBuilder, or OutputMocksConfig) into a NormalizedMocksConfig with
indexMockFiles and a generators array of GlobalMockOptions or
ClientMockBuilder. The mocks: true shorthand expands to the default MSW
plus default Faker generators.

Replace the singular generateMock in client.ts with invokeMockGenerator and
let generateOperations call it once per configured generator, populating
GeneratorOperation.mockOutputs with one entry per generator. Drop the
handlersDisabled branch from the client header/footer/title helpers; the
same intent is now expressed by omitting an msw entry from generators.

Update write-specs to filter out generated mock files by all configured
extensions (msw, faker, ...) when assembling the workspace index. Drop the
redundant mock field from api.ts, hono, and mcp pipelines since each entry
is now threaded through invokeMockGenerator individually. Rename the CLI
flag from --mock to --mocks to match the new option name.
Update hono, mcp, angular, and solid-start callers/tests for the new
NormalizedOutputOptions shape. The hono and mcp zod-generation paths no
longer pre-thread mock into GeneratorOptions (per-entry routing is handled
by invokeMockGenerator). Test fixtures across angular, solid-start, and
the faker combine suite move to the canonical
mocks: { indexMockFiles: false, generators: [] } default.

Drop the generateHandlers: false describe block from msw/index.test.ts
since that mode is replaced by omitting an msw entry from
output.mocks.generators.
Migrate every top-level output.mock declaration across tests/configs and
samples/** to the new output.mocks shape. Boolean shorthand (mock: true)
becomes mocks: true. Object form (mock: { type, ... }) is rewritten as
mocks: { generators: [{ type, ... }] }, with indexMockFiles hoisted onto
the mocks object. The CLI custom-builder example uses the function-form
mocks: (verbOptions, _) => ... shorthand.

Per-operation override.mock and override.operations.X.mock entries keep
their existing per-operation shape; only the top-level output.mock is
renamed.
…nd add v8 migration note

- Rename mocks: → mock: in all code examples across guides and reference docs
- Update ## mocks section heading to ## mock in output.mdx
- Fix stale comment in integration.mdx (old single-generator shape → generators array)
- Add pets.faker.ts to file tree in client-with-zod.mdx (mock: true emits both files)
- Add output.mock shape change to v8 breaking changes guide (section 10)
- Throw on function mock generators in tags-split mode instead of silently skipping
Validates that each mock type (msw, faker) appears at most once in
mock.generators. Duplicate entries would produce conflicting file
outputs, so we fail fast with a clear error message.

Also removes an unnecessary nullish coalescing on the required
generators field (lint: @typescript-eslint/no-unnecessary-condition).
@jakiestfu
jakiestfu force-pushed the feat/refactor-mocks branch from 4eb10c6 to 74fe6d6 Compare May 20, 2026 21:02
@jakiestfu

jakiestfu commented May 20, 2026

Copy link
Copy Markdown
Contributor Author

@melloware Restored mock property name, updated the V8 docs and also addressed the meaningful coderabbitai feedback.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/content/docs/guides/msw.mdx`:
- Line 243: Update the docs text to correct the config key typo: replace the
incorrect `mocks.indexMockFiles` reference with the actual option path
`output.mock.indexMockFiles` so the sentence reads that enabling
`output.mock.indexMockFiles` emits a root-level `index.<ext>.ts` (e.g.,
`index.msw.ts`) for each generator entry in `tags-split` mode.

In `@packages/angular/src/http-client.test.ts`:
- Line 49: The test fixture uses the wrong property name: change the object key
from "mocks" to "mock" in the normalized output fixture so it matches the
NormalizedOutputOptions shape and ensures output.mock is defined in generator
tests; locate the fixture object (where "mocks: { indexMockFiles: false,
generators: [] }" is declared in http-client.test.ts) and rename the key to
"mock" while keeping the inner structure the same.

In `@packages/angular/src/http-resource.test.ts`:
- Line 57: The test uses the wrong property name "mocks" in the normalized
output defaults (mocks: { indexMockFiles: false, generators: [] }), which should
be "mock"; update the property key from "mocks" to "mock" in the test
configuration so the normalized output defaults correctly apply (replace the
"mocks" property with "mock" in the object containing indexMockFiles and
generators).

In `@packages/core/src/types.ts`:
- Around line 442-450: The doc comment above the OutputMocksConfig interface
refers to a top-level "mocks" key but the public option is singular "mock";
update the comment and example to use "mock" (e.g., "mock: { indexMockFiles:
true, generators: [...] }") so the documentation matches the actual option name
and ensure any inline examples and descriptive text reference OutputMocksConfig
and the singular "mock" key consistently.

In `@packages/core/src/writers/split-mode.ts`:
- Around line 170-171: The loop in split-mode.ts incorrectly pairs mockOutputs
with generators by index (for ... of mockOutputs.entries()) which can misalign;
update the lookup to find the matching generator by type instead (like
single-mode.ts and tags-mode.ts) — inside the for loop replace direct access to
output.mock.generators[index] with a search such as
output.mock.generators.find(g => g.type === mockOutput.type), handle the case
where no generator is found (fallback or skip) and then use that found
generator's options when building the mock (ensure variables like
generatorOptions or generator are updated accordingly).

In `@packages/orval/src/utils/options.ts`:
- Around line 190-194: The code assumes mocksOption.generators exists and is an
array before calling .map, causing an opaque runtime crash; update the handling
in the branch that builds mocks (where mocks, mocksOption, indexMockFiles, and
generators are referenced) to validate mocksOption.generators with Array.isArray
before mapping: if it's an array, map and use isFunction(...) as before; if it's
missing or not an array, either default to an empty array or throw a clear,
descriptive configuration error indicating that "mocks.generators" must be an
array of generator functions; ensure the rest of the mocks object
(indexMockFiles) continues to use the nullish-coalesced default as currently
implemented.

In `@packages/solid-start/src/index.test.ts`:
- Line 32: In the test configuration object in
packages/solid-start/src/index.test.ts replace the incorrect property name
"mocks" with the singular "mock" so TypeScript recognizes the correct property;
locate the object literal containing mocks: { indexMockFiles: false, generators:
[] } and rename the key to mock: { indexMockFiles: false, generators: [] }
ensuring any references in the same file or nearby tests use the new "mock" key
consistently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7d01f3e1-e5a0-4d2f-a383-a3852de5cc08

📥 Commits

Reviewing files that changed from the base of the PR and between 095c898 and 74fe6d6.

⛔ Files ignored due to path filters (7)
  • samples/hono/hono-with-fetch-client/next-app/app/gen/pets/pets.faker.ts is excluded by !**/gen/**
  • samples/next-app-with-fetch/app/gen/pets/pets.faker.ts is excluded by !**/gen/**
  • samples/react-query/custom-fetch/src/gen/pets/pets.faker.ts is excluded by !**/gen/**
  • samples/svelte-query/custom-fetch/src/gen/pets/pets.faker.ts is excluded by !**/gen/**
  • samples/svelte-query/v6-custom-fetch/src/gen/pets/pets.faker.ts is excluded by !**/gen/**
  • samples/swr-with-zod/src/gen/endpoints/pets/pets.faker.ts is excluded by !**/gen/**
  • samples/vue-query/custom-fetch/src/gen/pets/pets.faker.ts is excluded by !**/gen/**
📒 Files selected for processing (99)
  • docs/content/docs/guides/client-with-zod.mdx
  • docs/content/docs/guides/msw.mdx
  • docs/content/docs/reference/configuration/output.mdx
  • docs/content/docs/reference/integration.mdx
  • docs/content/docs/versions/v8.mdx
  • packages/angular/src/http-client.test.ts
  • packages/angular/src/http-resource.test.ts
  • packages/core/src/test-utils/context.ts
  • packages/core/src/test-utils/split-modes.ts
  • packages/core/src/types.ts
  • packages/core/src/utils/assertion.ts
  • packages/core/src/utils/file-extensions.ts
  • packages/core/src/writers/mock-outputs.ts
  • packages/core/src/writers/single-mode.ts
  • packages/core/src/writers/split-mode.ts
  • packages/core/src/writers/split-tags-mode.ts
  • packages/core/src/writers/tags-mode.ts
  • packages/core/src/writers/target-tags.ts
  • packages/core/src/writers/target.ts
  • packages/hono/src/index.ts
  • packages/mcp/src/index.ts
  • packages/mock/src/delay.ts
  • packages/mock/src/faker/getters/combine.test.ts
  • packages/mock/src/faker/index.test.ts
  • packages/mock/src/faker/index.ts
  • packages/mock/src/index.ts
  • packages/mock/src/msw/index.test.ts
  • packages/mock/src/msw/index.ts
  • packages/orval/src/api.ts
  • packages/orval/src/bin/orval.ts
  • packages/orval/src/client.ts
  • packages/orval/src/utils/options.ts
  • packages/orval/src/write-specs.ts
  • packages/solid-start/src/index.test.ts
  • samples/angular-app/orval.config.ts
  • samples/angular-query/orval.config.ts
  • samples/hono/hono-with-fetch-client/__snapshots__/next-app/pets/pets.faker.ts
  • samples/next-app-with-fetch/__snapshots__/pets/pets.faker.ts
  • samples/react-app-with-swr/basic/__snapshots__/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • samples/react-app-with-swr/basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • samples/react-app-with-swr/fetch-client/__snapshots__/endpoints/swaggerPetstore.faker.ts
  • samples/react-app-with-swr/fetch-client/src/api/endpoints/swaggerPetstore.faker.ts
  • samples/react-app/__snapshots__/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • samples/react-app/src/api/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • samples/react-query/basic/__snapshots__/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • samples/react-query/basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • samples/react-query/custom-fetch/__snapshots__/pets/pets.faker.ts
  • samples/solid-start/basic/src/api/endpoints/petstore.faker.ts
  • samples/svelte-query/basic/__snapshots__/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • samples/svelte-query/basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • samples/svelte-query/custom-fetch/__snapshots__/pets/pets.faker.ts
  • samples/svelte-query/v6-custom-fetch/__snapshots__/pets/pets.faker.ts
  • samples/swr-with-zod/__snapshots__/endpoints/pets/pets.faker.ts
  • samples/vue-query/custom-fetch/__snapshots__/pets/pets.faker.ts
  • samples/vue-query/vue-query-basic/__snapshots__/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • samples/vue-query/vue-query-basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • skills/orval/SKILL.md
  • skills/orval/advanced-config.md
  • skills/orval/angular.md
  • skills/orval/hono.md
  • skills/orval/mocking-msw.md
  • skills/orval/solid-start.md
  • skills/orval/tooling-workflow.md
  • tests/__snapshots__/angular/split/endpoints.faker.ts
  • tests/__snapshots__/angular/tags-split/health/health.faker.ts
  • tests/__snapshots__/angular/tags-split/pets/pets.faker.ts
  • tests/__snapshots__/axios/petstore-tags-split-mutator/health/health.faker.ts
  • tests/__snapshots__/axios/petstore-tags-split-mutator/pets/pets.faker.ts
  • tests/__snapshots__/axios/petstore-tags-split/health/health.faker.ts
  • tests/__snapshots__/axios/petstore-tags-split/pets/pets.faker.ts
  • tests/__snapshots__/axios/split/endpoints.faker.ts
  • tests/__snapshots__/default/issue-2998/documents/documents.faker.ts
  • tests/__snapshots__/default/issue-2998/requests/requests.faker.ts
  • tests/__snapshots__/default/override-mock/endpoints.faker.ts
  • tests/__snapshots__/fetch/petstore-tags-split/health/health.faker.ts
  • tests/__snapshots__/fetch/petstore-tags-split/pets/pets.faker.ts
  • tests/__snapshots__/fetch/split/endpoints.faker.ts
  • tests/__snapshots__/mock/petstore-tags-split/health/health.faker.ts
  • tests/__snapshots__/mock/petstore-tags-split/pets/pets.faker.ts
  • tests/__snapshots__/mock/split/endpoints.faker.ts
  • tests/__snapshots__/react-query/importFromSubdirectory/endpoints.faker.ts
  • tests/__snapshots__/react-query/petstore-tags-split/health/health.faker.ts
  • tests/__snapshots__/react-query/petstore-tags-split/pets/pets.faker.ts
  • tests/__snapshots__/react-query/split/endpoints.faker.ts
  • tests/__snapshots__/svelte-query/petstore-tags-split/health/health.faker.ts
  • tests/__snapshots__/svelte-query/petstore-tags-split/pets/pets.faker.ts
  • tests/__snapshots__/svelte-query/split/endpoints.faker.ts
  • tests/__snapshots__/swr/petstore-tags-split/health/health.faker.ts
  • tests/__snapshots__/swr/petstore-tags-split/pets/pets.faker.ts
  • tests/__snapshots__/swr/split/endpoints.faker.ts
  • tests/__snapshots__/vue-query/petstore-tags-split/health/health.faker.ts
  • tests/__snapshots__/vue-query/petstore-tags-split/pets/pets.faker.ts
  • tests/__snapshots__/vue-query/split/endpoints.faker.ts
  • tests/__snapshots__/zod/petstore-tags-split/pets/pets.faker.ts
  • tests/__snapshots__/zod/split/endpoints.faker.ts
  • tests/configs/default.config.ts
  • tests/configs/mock.config.ts
  • tests/configs/react-query.config.ts
  • tests/configs/swr.config.ts
💤 Files with no reviewable changes (3)
  • packages/mcp/src/index.ts
  • packages/hono/src/index.ts
  • packages/orval/src/api.ts
✅ Files skipped from review due to trivial changes (52)
  • packages/core/src/test-utils/context.ts
  • skills/orval/solid-start.md
  • skills/orval/angular.md
  • docs/content/docs/versions/v8.mdx
  • skills/orval/tooling-workflow.md
  • tests/snapshots/react-query/importFromSubdirectory/endpoints.faker.ts
  • packages/mock/src/faker/getters/combine.test.ts
  • docs/content/docs/reference/integration.mdx
  • tests/snapshots/angular/tags-split/health/health.faker.ts
  • samples/react-app-with-swr/basic/snapshots/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • tests/snapshots/default/issue-2998/documents/documents.faker.ts
  • tests/snapshots/react-query/petstore-tags-split/health/health.faker.ts
  • samples/react-app/src/api/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • tests/snapshots/mock/petstore-tags-split/health/health.faker.ts
  • skills/orval/hono.md
  • samples/react-app-with-swr/basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • samples/react-app-with-swr/fetch-client/snapshots/endpoints/swaggerPetstore.faker.ts
  • tests/snapshots/fetch/petstore-tags-split/health/health.faker.ts
  • packages/orval/src/bin/orval.ts
  • samples/react-app/snapshots/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • tests/snapshots/fetch/petstore-tags-split/pets/pets.faker.ts
  • tests/snapshots/fetch/split/endpoints.faker.ts
  • tests/snapshots/vue-query/petstore-tags-split/pets/pets.faker.ts
  • skills/orval/advanced-config.md
  • skills/orval/mocking-msw.md
  • tests/snapshots/swr/petstore-tags-split/pets/pets.faker.ts
  • tests/snapshots/react-query/split/endpoints.faker.ts
  • tests/snapshots/react-query/petstore-tags-split/pets/pets.faker.ts
  • tests/snapshots/svelte-query/split/endpoints.faker.ts
  • docs/content/docs/reference/configuration/output.mdx
  • samples/svelte-query/basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • samples/react-query/custom-fetch/snapshots/pets/pets.faker.ts
  • tests/snapshots/default/override-mock/endpoints.faker.ts
  • tests/snapshots/axios/petstore-tags-split/health/health.faker.ts
  • tests/snapshots/axios/petstore-tags-split/pets/pets.faker.ts
  • tests/snapshots/vue-query/split/endpoints.faker.ts
  • samples/react-query/basic/snapshots/endpoints/petstoreFromFileSpecWithTransformer.faker.ts
  • samples/svelte-query/v6-custom-fetch/snapshots/pets/pets.faker.ts
  • samples/vue-query/custom-fetch/snapshots/pets/pets.faker.ts
  • tests/snapshots/zod/petstore-tags-split/pets/pets.faker.ts
  • samples/react-app-with-swr/fetch-client/src/api/endpoints/swaggerPetstore.faker.ts
  • tests/snapshots/axios/petstore-tags-split-mutator/pets/pets.faker.ts
  • tests/snapshots/zod/split/endpoints.faker.ts
  • tests/snapshots/mock/split/endpoints.faker.ts
  • tests/snapshots/default/issue-2998/requests/requests.faker.ts
  • tests/snapshots/mock/petstore-tags-split/pets/pets.faker.ts
  • samples/svelte-query/custom-fetch/snapshots/pets/pets.faker.ts
  • tests/snapshots/swr/split/endpoints.faker.ts
  • samples/swr-with-zod/snapshots/endpoints/pets/pets.faker.ts
  • skills/orval/SKILL.md
  • samples/next-app-with-fetch/snapshots/pets/pets.faker.ts
  • tests/snapshots/svelte-query/petstore-tags-split/pets/pets.faker.ts

Comment thread docs/content/docs/guides/msw.mdx Outdated
Comment thread packages/angular/src/http-client.test.ts Outdated
Comment thread packages/angular/src/http-resource.test.ts Outdated
Comment thread packages/core/src/types.ts Outdated
Comment thread packages/core/src/writers/split-mode.ts Outdated
Comment thread packages/orval/src/utils/options.ts
Comment thread packages/solid-start/src/index.test.ts Outdated
jakiestfu added 2 commits May 20, 2026 21:13
The index-based pairing of mockOutputs to generators was fragile —
mockOutputs are accumulated by type insertion order in target.ts, which
may not match the user's generator array order. Now all four writer
modes (single, split, tags, tags-split) consistently use .find() by
type to match each mockOutput to its generator entry.

Also adds a runtime TypeError when mock.generators is missing or not an
array, preventing opaque crashes from malformed configs.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
packages/core/src/writers/split-tags-mode.ts (1)

49-53: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reject function mock generators before filtering them out.

This mode still accepts output.mock.generators from normalization, but this filter silently removes every ClientMockBuilder. In tags-split mode that turns a supported config shape into a no-op: no per-tag mock files and no index files are written. The PR behavior should fail fast here instead.

🔧 Suggested guard
+  if (output.mock.generators.some(isFunction)) {
+    throw new Error(
+      'Function mock generators are not supported in tags-split mode. Use typed generators (e.g. { type: "msw" } or { type: "faker" }).',
+    );
+  }
+
   const generatorEntries = output.mock.generators.filter(
     (g): g is GlobalMockOptions => !isFunction(g),
   );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/writers/split-tags-mode.ts` around lines 49 - 53, Reject
function-type mock generators early instead of silently filtering them out: in
split-tags-mode, inspect output.mock.generators for any entries where
isFunction(g) is true (these represent ClientMockBuilder/function generators)
and throw a clear error explaining that tags-split mode does not support
function mock generators. Keep the existing filter that produces
generatorEntries (const generatorEntries = output.mock.generators.filter((g): g
is GlobalMockOptions => !isFunction(g))), but add a pre-check that iterates
output.mock.generators, detects any function entries, and raises a
validation/throw with context (mentioning tags-split mode and ClientMockBuilder)
so the PR fails fast rather than becoming a no-op.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/core/src/writers/split-mode.ts`:
- Around line 174-179: The loop over mockOutputs ignores function-based mocks
because the find only matches typed GlobalMockOptions; update the logic in
split-mode.ts inside the for (const mockOutput of mockOutputs) loop to also
detect and handle function generators from output.mock.generators (use
isFunction to find them) when entry === undefined: if a function generator
exists, treat it as the source (invoke it or derive its result the same way
normalizeOptions would) and proceed to emit the mock file, otherwise fail-fast
with a clear error; reference the variables entry, output.mock.generators,
isFunction, GlobalMockOptions so the change is applied where the current
typed-only lookup occurs.

---

Duplicate comments:
In `@packages/core/src/writers/split-tags-mode.ts`:
- Around line 49-53: Reject function-type mock generators early instead of
silently filtering them out: in split-tags-mode, inspect output.mock.generators
for any entries where isFunction(g) is true (these represent
ClientMockBuilder/function generators) and throw a clear error explaining that
tags-split mode does not support function mock generators. Keep the existing
filter that produces generatorEntries (const generatorEntries =
output.mock.generators.filter((g): g is GlobalMockOptions => !isFunction(g))),
but add a pre-check that iterates output.mock.generators, detects any function
entries, and raises a validation/throw with context (mentioning tags-split mode
and ClientMockBuilder) so the PR fails fast rather than becoming a no-op.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 158e927c-bbd6-45e0-b9b9-d8877f924050

📥 Commits

Reviewing files that changed from the base of the PR and between 2b7e01c and 8e70924.

📒 Files selected for processing (5)
  • docs/content/docs/guides/msw.mdx
  • packages/core/src/types.ts
  • packages/core/src/writers/split-mode.ts
  • packages/core/src/writers/split-tags-mode.ts
  • packages/orval/src/utils/options.ts
✅ Files skipped from review due to trivial changes (1)
  • docs/content/docs/guides/msw.mdx

Comment on lines +174 to +179
for (const mockOutput of mockOutputs) {
const entry = output.mock.generators.find(
(g): g is GlobalMockOptions =>
!isFunction(g) && g.type === mockOutput.type,
);
if (!entry) continue;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't silently drop mock: fn in split mode.

normalizeOptions still normalizes a function-based mock config into output.mock.generators, but this lookup only matches typed entries. With mock: () => ..., entry is always undefined, so split mode emits no mock file instead of preserving the old behavior or failing fast.

🔧 Minimal safeguard
+    if (output.mock.generators.some(isFunction)) {
+      throw new Error(
+        'Function mock generators are not supported in split mode. Use typed generators (e.g. { type: "msw" } or { type: "faker" }).',
+      );
+    }
+
     const mockPaths: string[] = [];
     for (const mockOutput of mockOutputs) {
       const entry = output.mock.generators.find(
         (g): g is GlobalMockOptions =>
           !isFunction(g) && g.type === mockOutput.type,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/writers/split-mode.ts` around lines 174 - 179, The loop
over mockOutputs ignores function-based mocks because the find only matches
typed GlobalMockOptions; update the logic in split-mode.ts inside the for (const
mockOutput of mockOutputs) loop to also detect and handle function generators
from output.mock.generators (use isFunction to find them) when entry ===
undefined: if a function generator exists, treat it as the source (invoke it or
derive its result the same way normalizeOptions would) and proceed to emit the
mock file, otherwise fail-fast with a clear error; reference the variables
entry, output.mock.generators, isFunction, GlobalMockOptions so the change is
applied where the current typed-only lookup occurs.

Adds a pre-check before filtering generators so that function-based
entries (ClientMockBuilder) cause a clear error instead of being
silently dropped.
@jakiestfu

Copy link
Copy Markdown
Contributor Author

@melloware Ok now I'm done. The rabbit had more to say.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/core/src/writers/split-tags-mode.ts (1)

254-259: 💤 Low value

Consider using generatorEntries instead of re-filtering.

Since lines 49-56 now throw if any function generators exist, the !isFunction(g) check here is redundant. Using the already-filtered generatorEntries would be cleaner.

♻️ Suggested simplification
         for (const mockOutput of mockOutputs) {
-          const entry = output.mock.generators.find(
-            (g): g is GlobalMockOptions =>
-              !isFunction(g) && g.type === mockOutput.type,
-          );
+          const entry = generatorEntries.find(
+            (g) => g.type === mockOutput.type,
+          );
           if (!entry) continue;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/writers/split-tags-mode.ts` around lines 254 - 259, The
loop is re-filtering output.mock.generators with a redundant !isFunction check;
replace the find over output.mock.generators with a lookup over the
already-filtered generatorEntries (which contain only GlobalMockOptions) to
simplify and avoid duplicate filtering—use generatorEntries.find(g => g.type ===
mockOutput.type) (referencing mockOutputs, generatorEntries,
output.mock.generators, and isFunction) and remove the redundant type
guard/continuation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/core/src/writers/split-tags-mode.ts`:
- Around line 254-259: The loop is re-filtering output.mock.generators with a
redundant !isFunction check; replace the find over output.mock.generators with a
lookup over the already-filtered generatorEntries (which contain only
GlobalMockOptions) to simplify and avoid duplicate filtering—use
generatorEntries.find(g => g.type === mockOutput.type) (referencing mockOutputs,
generatorEntries, output.mock.generators, and isFunction) and remove the
redundant type guard/continuation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 439b74e4-6020-46d9-9ff8-8cd9902acb32

📥 Commits

Reviewing files that changed from the base of the PR and between 8e70924 and 881e03c.

📒 Files selected for processing (1)
  • packages/core/src/writers/split-tags-mode.ts

@melloware melloware added this to the 8.12.0 milestone May 20, 2026
@melloware
melloware merged commit b059ca6 into orval-labs:master May 20, 2026
6 checks passed
@jakiestfu

Copy link
Copy Markdown
Contributor Author

Wild, thank you!

@melloware

Copy link
Copy Markdown
Collaborator

This is a great change.

@ingvaldlorentzen

Copy link
Copy Markdown
Contributor

Looks awesome! Thank you so much ♥️

Looking forward to the release!

@jakiestfu

Copy link
Copy Markdown
Contributor Author

Thanks again folks! @melloware Do you have an estimate for when 8.12.0 will land?

@melloware

Copy link
Copy Markdown
Collaborator

,I can probably do it today

@melloware

Copy link
Copy Markdown
Collaborator

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking change Breaking change on upgrade mock Related to mock generation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Ability to generate faker mocks independent to msw

3 participants